package com.example.androidaudiovideodemo.activity

import android.Manifest
import android.content.Intent
import android.graphics.Color
import android.media.*
import android.net.Uri
import android.os.Build
import androidx.core.content.FileProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.blankj.utilcode.constant.TimeConstants
import com.blankj.utilcode.util.*
import com.example.androidaudiovideodemo.AudioListAdapter
import com.example.androidaudiovideodemo.AudioListBean
import com.example.androidaudiovideodemo.R
import com.example.androidaudiovideodemo.base.BaseActivity
import com.example.androidaudiovideodemo.utils.Pcm2WavUtil
import kotlinx.android.synthetic.main.activity_audio_record_trace.*
import java.io.*
import java.lang.Exception
import java.lang.StringBuilder
import kotlin.concurrent.thread
import kotlin.math.abs

/**
 *
 * author: renbing
 *
 * date: 2021/12/13
 *
 * des:2. 在 Android 平台使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放，并实现读写音频 wav 文件
 */
class AudioRecordAndTrackActivity : BaseActivity(){

    private var mBufferSize = 0
    private var mAudioRecord: AudioRecord? = null
    private var mAudioTrack: AudioTrack? = null
    private var mDataBuffer: ByteArray? = null

    private var mStatus = 0

    private val mAudioDatas = arrayListOf<AudioListBean>()
    private var mAdapter: AudioListAdapter<AudioListBean>? = null
    private var mAudioFile: File? = null
    private var mAudioFileWAV: File? = null
    private var mStartRecordTime = 0L
    private var mEndRecordTime = 0L
    private var mAudioDirectoryPath: String? = null
    private var mIsStaticMode = false
    private var mLastSelectedPosition = -1

    override fun getLayoutId(): Int = R.layout.activity_audio_record_trace

    override fun performInit() {
        super.performInit()

        setStatusBarColor(this,true)
    }

    override fun initData() {
        super.initData()

        task_two_title_view.run {
            setLeftIconColorFilter(Color.BLACK)
            setOnLeftIconClickListener {
                finish()
            }
        }

        mBufferSize = AudioRecord.getMinBufferSize(44100,AudioFormat.CHANNEL_IN_STEREO,AudioFormat.ENCODING_PCM_16BIT)
        mDataBuffer = ByteArray(mBufferSize)

        PermissionUtils.permission(Manifest.permission.RECORD_AUDIO,
                                   Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                   Manifest.permission.READ_EXTERNAL_STORAGE)
            .callback { isAllGranted, granted, deniedForever, denied ->
            granted.forEach {
                if (it == Manifest.permission.RECORD_AUDIO){
                    mAudioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,44100, AudioFormat.CHANNEL_IN_STEREO,AudioFormat.ENCODING_PCM_16BIT,mBufferSize)
                }
            }
        }.request()

        mAudioDirectoryPath = SDCardUtils.getSDCardPathByEnvironment()+"/$packageName/audio"
        val audioDirectory = File(mAudioDirectoryPath?:"")
        if (!audioDirectory.exists()){
            audioDirectory.mkdirs()
        }
        val files = audioDirectory.listFiles()
        files?.forEach {
            val fileCreateTm = Regex("\\d{13}").find(it?.name?:"")?.value?.toLong()?:System.currentTimeMillis()
            val fileCreateTime = TimeUtils.millis2String(fileCreateTm, "yy-MM-dd HH:mm")
            val audioListBean = AudioListBean().apply {
                name = it.name
                time = fileCreateTime
//                duration = "1秒"
                size = FileUtils.getSize(it)
            }
            mAudioDatas.add(0,audioListBean)
        }

        mAdapter = AudioListAdapter(this,mAudioDatas,{position, data ->
            audioPlay(data)
        }){position, data ->
            if (mLastSelectedPosition != -1){
                mAudioDatas[mLastSelectedPosition].isSelect = false
                mAdapter?.notifyItemRangeChanged(mLastSelectedPosition,1)
            }
            mAudioDatas[position].isSelect = true
            mAdapter?.notifyItemRangeChanged(position,1)
            mLastSelectedPosition = position
        }
        audio_list_rv.apply {
            adapter = mAdapter
            layoutManager = LinearLayoutManager(this@AudioRecordAndTrackActivity)
        }
    }

    /**
     * 音频播放
     */
    private fun audioPlay(data: AudioListBean) {
        val listName = data.name
        val cacheAudioDirectory =
            FileUtils.getFileByPath(mAudioDirectoryPath) ?: File(mAudioDirectoryPath ?: "")
        if (!cacheAudioDirectory.isDirectory) {
            ToastUtils.showShort("文件已损坏")
            return
        }
        val cacheFiles = cacheAudioDirectory.listFiles() ?: arrayOf()
        if (cacheFiles.isEmpty()) {
            ToastUtils.showShort("文件已损坏")
            return
        }
        cacheFiles.forEach {
            if (it.name == listName) {
                if (mAudioRecord?.state != AudioTrack.STATE_UNINITIALIZED){
                    mAudioTrack?.stop()
                    mAudioTrack?.release()
                }
                if (listName?.endsWith(".pcm") == true){
                    if (mIsStaticMode){
                        playByStaticMode(listName)
                    }else{
                        playByStreamMode(listName)
                    }
                } else{
                    playWav(listName)
                }
            }
        }
    }

    /**
     * 播放wav音频文件
     */
    private fun playWav(listName: String?){
        val wavFile = File(mAudioDirectoryPath ?: "", listName ?: "")
        if (!wavFile.exists()) return
        val intent = Intent(Intent.ACTION_VIEW)
        val uri = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
            FileProvider.getUriForFile(this,"com.example.androidaudiovideodemo.fileprovider",wavFile)
        } else{
            Uri.fromFile(wavFile.absoluteFile)
        }
        intent.setDataAndType(uri,"audio")
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        startActivity(intent)
    }

    /**
     * 以MODE_STREAM播放音频
     * 从音频文件实时将数据读入缓存区buffer中，有一定延时性，处理一些延时要求不高，音频文件较大的情况
     */
    private fun playByStreamMode(listName: String?) {
        mAudioTrack = AudioTrack(
            AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build(),
            AudioFormat.Builder()
                .setSampleRate(44100)
                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                .setChannelMask(AudioFormat.CHANNEL_IN_STEREO)
                .build(),
            mBufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE
        )
        mAudioTrack?.play()

        val playFile = File(mAudioDirectoryPath ?: "", listName ?: "")
        val fis: FileInputStream?
        try {
            fis = FileInputStream(playFile)
            thread {
                try {
                    val tmpBuffer = ByteArray(mBufferSize)
                    while (fis.available() > 0) {
                        val readCount = fis.read(tmpBuffer)
                        if (readCount == AudioTrack.ERROR_INVALID_OPERATION || readCount == AudioTrack.ERROR_BAD_VALUE) continue
                        if (readCount > 0) {
                            mAudioTrack?.write(tmpBuffer, 0, readCount)
                        }
                    }
                    fis.close()
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 已MODE_STATIC播放音频
     * 从音频文件一次性将数据读入缓存区buffer中，处理一些延时要求高，音频文件不大的情况
     */
    private fun playByStaticMode(listName: String?){
        thread {
            mAudioTrack = AudioTrack(
                AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build(),
                AudioFormat.Builder()
                    .setSampleRate(44100)
                    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                    .setChannelMask(AudioFormat.CHANNEL_IN_STEREO)
                    .build(),
                mBufferSize, AudioTrack.MODE_STATIC, AudioManager.AUDIO_SESSION_ID_GENERATE
            )
            val playFile = File(mAudioDirectoryPath ?: "", listName ?: "")
            val fis: FileInputStream?
            try {
                fis = FileInputStream(playFile)
                val out = ByteArrayOutputStream()
                var read: Int
                var audioData: ByteArray? = null
                while (fis.read().also { read = it } != -1){
                    out.write(read)
                    audioData = out.toByteArray()
                }
                fis.close()
                if (audioData == null){
                    ToastUtils.showShort("文件已损坏")
                    return@thread
                }
                mAudioTrack?.write(audioData,0, audioData.size)
                mAudioTrack?.play()
            } catch (e: Exception){
                e.printStackTrace()
            }
        }
    }

    override fun initListener() {
        super.initListener()

        start_record.setOnClickListener {
            if (mStatus == 1) return@setOnClickListener
            if (mAudioRecord?.state == AudioRecord.STATE_UNINITIALIZED){
                mAudioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,44100, AudioFormat.CHANNEL_IN_STEREO,AudioFormat.ENCODING_PCM_16BIT,mBufferSize)
            }

            val currentTm = System.currentTimeMillis()
            mAudioFile = File(mAudioDirectoryPath,"test_audio_$currentTm.pcm")
            mAudioFileWAV = File(mAudioDirectoryPath,"test_audio_$currentTm.wav")
            if (!mAudioFile?.exists()!!){
                mAudioFile?.createNewFile()
            }
            if (!mAudioFileWAV?.exists()!!){
                mAudioFileWAV?.createNewFile()
            }
            mAudioRecord?.startRecording()
            mStatus = 1
            mStartRecordTime = System.currentTimeMillis()
            thread {
                var os: FileOutputStream? = null
                var osWav: FileOutputStream? = null
                val wavRaf: RandomAccessFile?
                try {
                    os = FileOutputStream(mAudioFile)
                    osWav = FileOutputStream(mAudioFileWAV)
                    val headers = Pcm2WavUtil.generateWavFileHeader(0,44100,mAudioRecord?.channelCount?:0)
                    osWav.write(headers,0,headers.size)
                    while (mStatus == 1){
                        val data = mAudioRecord?.read(mDataBuffer!!,0,mBufferSize)
                        if (data != AudioRecord.ERROR_INVALID_OPERATION){
                            os.write(mDataBuffer)
                            osWav.write(mDataBuffer)
                        }
                    }
                    os.flush()
                    osWav.flush()
                    //修改头部
                    wavRaf = RandomAccessFile(mAudioFileWAV,"rw")
                    val header = Pcm2WavUtil.generateWavFileHeader(mAudioFile?.length()?:0,44100,mAudioRecord?.channelCount?:0)
                    wavRaf.seek(0)
                    wavRaf.write(header)
                }catch (e: Exception){
                    e.printStackTrace()
                }finally {
                    os?.close()
                    osWav?.close()
                }
            }
        }
        stop_record.setOnClickListener {
            if (mStatus == 3) return@setOnClickListener
            mAudioRecord?.release()
            mStatus = 3
            mEndRecordTime = System.currentTimeMillis()
            val fileName = mAudioFile?.name
            val wavFileName = mAudioFileWAV?.name
            val fileCreateTm = Regex("\\d{13}").find(mAudioFile?.name?:"")?.value?.toLong()?:System.currentTimeMillis()
            val fileCreateTime = TimeUtils.millis2String(fileCreateTm, "yy-MM-dd HH:mm")
            val duration = TimeUtils.getTimeSpan(mEndRecordTime,mStartRecordTime,TimeConstants.SEC)
            val audioBean = AudioListBean(fileName,FileUtils.getSize(mAudioFile),fileCreateTime,transformDuration(duration))
            val audioBeanWAV = AudioListBean(wavFileName,FileUtils.getSize(mAudioFileWAV),fileCreateTime,transformDuration(duration))
            mAudioDatas.add(0,audioBean)
            mAudioDatas.add(0,audioBeanWAV)
            audio_list_rv.smoothScrollToPosition(0)
            mAdapter?.notifyItemRangeInserted(0,2)
            mAudioFile = null
        }
//        pause_record.setOnClickListener {
//
//        }

        play_mode.setOnCheckedChangeListener { buttonView, isChecked ->
            mIsStaticMode = isChecked
        }
    }

    /**
     * 转换录音时长
     */
    private fun transformDuration(duration: Long): String{
        val d = abs(duration)
        val sb = StringBuilder()
        when{
            d <= 60 -> sb.append(d).append("秒")
            d in 61..3600 -> {
                sb.append(d/60).append("分")
                if (d%60 != 0L){
                    sb.append(d%60).append("秒")
                }
            }
            d > 3600 -> {
                sb.append(d/3600).append("时")
                val minute = d%3600
                if (minute != 0L){
                    sb.append(minute/60).append("分")
                }
                if (minute%60 != 0L){
                    sb.append(minute%60).append("秒")
                }
            }
        }

        return sb.toString()
    }

    override fun onPause() {
        super.onPause()

        mAudioRecord?.release()
        mAudioRecord = null
        mAudioTrack?.release()
        mAudioTrack = null
    }
}